Test technique pour le stage data science chez Bouygues Telecom

Gabriel Watkinson


Ce notebook contient le test technique pour le stage de data science chez Bouygues Telecom basée sur des données factices sur la résiliation des clients ayant un abonnement télécom.

Description du test

L'objectif est de comprendre et de prédire la résilation des clients. Pour ce faire 4 datasets sont fournis :

Organisation du notebook

Le notebook est composé de 3 parties et d'une conclusion :

  1. Analyses des données et statistiques descriptives
  2. Préparation de données
  3. Modélisation du problème

Analyses des données et statistiques descriptives

Dans un premier temps, nous analyserons les données pour comprendre leur contenu et leur structure en utilisant des statistiques descriptives et des graphiques pour visualiser les distributions des données, ainsi que les problèmes éventuels, comme les données manquantes par exemple, qui devront être pris en compte lors de la modélisation.

EDA automatique avec pandas_profiling

Pour ce faire, nous allons utiliser pandas_profiling qui permet d'explorer les données et de soulever certain problèmes de manière automatique. Puis nous allons regarder manuellement certaines corrélations. Cela va créer des profils avec des informations sur chaques variables (moyenne, médiane, valeurs manquantes, etc.), mais aussi sur quelques intéractions entre les variables (corrélations, etc.).

Ces profils sont ensuite exportés en format html dans le dossier nommé profiles.

Les données sont chargées à partir du dossier data qui se situe dans la racine du projet (comme ce notebook).

Profil resiliation_client

Nous commençons avec la table qui contient les données clients.

La table resiliation_client contient des données personnelles sur les clients, à savoir leur genre, leur situation familliale (en couple, parent) et une indication sur leur âge. Il y a également une information sur leur ancienneté chez Bouygues Telecom (en mois je suppose).

Le profil donne un certain nombre d'informations interessantes :

De plus, il peut être intéressant de noter la forte corrélation entre le fait d'être en couple et la situation parentale. En effet, le graphique suivant montre que, parmi les clients en couple, la moitié sont parents, alors que parmi les clients qui ne sont pas en couple, la grosse majorité n'est pas parent.

De même, on voit clairement que les clients les plus anciens sont plus souvent en couple que les nouveaux clients. On peut donc penser que les clients célibataires sont plus enclins à résilier leur abonnement alors que les clients en couples le font moins souvent.

Profil resiliation_forfait

Pour la table avec les informations sur les forfaits, il n'est pas nécessaire de faire un profil. On peut simplement regarder les données car il n'y a que quelques lignes.

En effet, cette table contient uniquement la quantité de Go par mois pour les différents forfaits mobile.

Profil resiliation_contrat

Regardons ensuite la table resiliation_contrat qui contient les informations sur les contrats des clients.

La table resiliation_contrat contient les données sur les contrats souscrits par les clients, à savoir le forfait, la durée du contrat (mensuel, annuel, biannuel), la méthode de paiement, la facture mensuelle moyenne, la facture totale et surtout si le client a résilé son abonnement ou non.

Le profil donne un certain nombre d'informations interessantes :

Maintenant, regardons quelques interactions entre les variables. Nous d'abord ajouter l'ancienneté au dataset.

Sur le graphique de gauche, on voit que l'ancienneté des clients a un effet très important sur la résiliation, en effet, les clients anciens ont moins tendance à résilier leur abonnement que les nouveaux clients.

Dans un deuxième temps, le graphique de droite montre que, quelque soit le forfait, les clients qui ont résilier avaient une facture mensuelle plus faible que les clients qui n'ont pas résilier. Cela peut être dû à des promotions à durée fixe, et donc, une fois cette durée terminée, les clients ont tendance à résilier leur abonnement. Ce qui est en accordance avec le premier graphique.

Ensuite, on peut voir que les données avec le forfait manquant sont très différents des autres en terme de facture mensuelle. En effet, les factures se situent entre le forfait à 20Go et celui à 80Go. On note clairement que la facture mensuelle est très corrélée à la quantité de Go. On peut donc supposer que les forfait manquants correspondent à celui qui n'est pas utilisé et qui contient 50Go. Cette hypothèse sera retenue lors du traitement des valeurs manquantes.

Sur le graphique suivant, on voit que les longs contrats sont moins susceptibles d'être résilier que les contrats courts.

Finalement, le dernier graphique montre que les gros abonnements à 200Go sont plus susceptibles de résilier que les autres abonnements.

Profil resiliation_option

La table resiliation_option contient les données sur les options ajoutées aux forfaits par les clients, notamment des options de téléphonie, de télévision, de sécurité, de support ou de stream.

Les seules valeurs manquantes sont dans les options de streaming, qui sont corrélées, en effet, si une des options est manquantes, l'autre l'ait également.

On remarque que toutes les options sont très corrélées avec la variable service_internet. En effet, si le service internet n'est pas inclu dans le forfait, aucune options ne peut l'être. Cependant, ce ne sont pas des variables redondantes, car il y a tout de même des options choisies par certains clients.

On peut faire la même remarque pour l'option téléphonie et les lignes multiples.

Sur le graphique suivant, on constate encore que les clients avec un forfait manquant sont uniques, en effet, aucun n'ont l'option de service telephonique. Cela renforce l'idée que les forfaits manquants sont dans une catégorie distincte, plutôt que des valeurs manquantes réparties entre les différents forfaits.

Préparation de données

Dans cette partie, nous allons travailler les données et les préparer pour qu'elles puissent être utilisées dans nos modèles.

Nous allons donc traiter le cas des valeurs manquantes et de la transformation des variables catégorielles en variables numériques.

Commençons par regrouper les données dans un dataframe nommé df en vérifiant que les id_client sont bien identiques.

On constate donc bien que les id_client sont des clés primaires et que les jointures sont bien en one to one.

Pour les forfaits, comme mentionné précedemment, on voit qu'il y en a qui sont manquants et qu'un forfait n'est pas du tout présent.

Générons le profil général, qui peut être interressant pour les corrélations.

Traitement des variables

Pour commencer, nous allons évoquer les méthodes possibles pour traiter les différentes varibles.

Nous allons donc regarder variables par variables ce qu'il est necéssaire de faire :

On va tout d'abord séparer les données en deux groupes : les données d'entrainement et les données de test.

Maintenant, nous allons utiliser sklearn-pandas pour associer un ou plusieurs estimateurs pour chaque variable catégorielles, afin de les transformer en features utilisable par nos modèles.

Par exemple, si on applique ce mapper au dataset X_train, on obtient :

Il est également possible de faire un autre mapper afin d'avoir un autre encoding.

Ce mapper renvoie une dataframe très différente, en effet, les colonnes ne sont pas dupliquées pour faire du OneHotEncoding, de plus la stratégie pour les valeurs manquantes est différente. Elle sont remplacées par la valeurs moyenne de la variable target.

Modélisation

Nous allons maintenant passer à la modélisation. Dans un premier temps, nous mettrons en place un modèle de base (régression logistique) pour avoir un premier score.

La métrique utilisée sera le recall. En effet, le recall semble important car il décrit le nombre de clients qui résilient, et qui n'ont pas été prédit comme tel par le modèle. Cela est donc utile si l'objectif est de conserver les clients le plus possible.

Par exemple, si on veut prévoir des clients à integrer dans une campagne de démarchage, ce modèle fournira la majorité des clients qui sont très suceptibles de résilier, en plus d'une quantité non négligeable de clients qui n'avait pas l'intention de résilier. Mais cela permet quand même de réduire drastiquement le scope de l'opération de démarchage.

Mais, il reste important de regarder la précision pour ne pas tout prédire comme résilient. Pour cela, on peut regarder le f1 score.

Modèle de base

Premièrement, nous allons utiliser un modèle de base, afin d'avoir une idée de la performance d'un modèle simple, en l'occurence, un modèle de régression logistique.

On voit donc que les deux preprocessing des données donnent des résultats similaires pour une regression logistique (le mapper 1 fonctionne un tout petit peu mieux que le mapper 2).

Le score f1 est à 0.63, ce qui est assez moyen, et surtout, le recall est très faible. Sur les 373 clients qui ont résilié dans le test set, il y a seulement 217 clients qui ont été prédit comme tel. Ce qui fait que l'on perdrait beacoup de clients.

Ce problème est encore pire si on regarde un RandomForest classique.

Il faut donc essayer de gérer le problème de déséquilibre des classes. Pour cela, on peut utiliser des technique de sampling qui enlève des données de la classes dominantes de manière aléatoire, ou rajoute des observations. Pour ce faire, on va utiliser le package imblearn qui contient des stratégies de sampling.

On voit clairement, que cette technique permet d'améliorer de manière significative le recall, qui passe d'environ 50% à presque 90% avec le sampler SMOTEEN. Cepandant, la précision diminue significativement et le score f1 reste autour de 0.6.

Dans la suite, nous allons utiliser le sampler SMOTEEN.

Essai de XGBoost, LightGBM, réseaux de neurones ...

Ici, nous allons de tester différents modèles de classification, pour voir s'il est possible d'améliorer le modèle de base.

Ainsi, l'estimateur AdaBoost, la regression logistique et le réseau de neurones semblent les plus performant en terme de recall, cependant, AdaBoost a un meilleur score f1 que les deux autres. Nous allons donc utiliser celui-ci, avec le mapper 1 et le sampler SMOTEEN.

Optimisation des hyperparamètres

Maintenant, nous allons voir s'il est possible d'améliorer le modèle en optimisant les hyperparamètres.

On voit que les meilleurs paramètres sont différents des valeurs par défaut, qui sont 50 estimateurs et 1.0 pour le paramètre learning_rate.

Cependant, quand on compare les deux matrices de confusion, on voit que le premier modèle est légerement meilleur que le second. Par contre, le modèle issu de la GridSearch a un meilleur score f1. Il reste à voir si la performance ajoutée avec ce modèle est suffisante pour compenser la complexité fortement accrue. En effet, le modèle de classification le plus simple fonctionnait déjà très bien, il faut voir si on peut l'optimiser un peu et s'il devient meilleur.

Au final, on vient bien que la régression linéaire est très performante bien que c'est un modèle très simple. Je vais donc choisir ce modèle car il sera plus simple à expliquer et à interpréter.

Explicabilité du modèle sélectionné

Dans cette partie, nous allons nous pencher plus en détails sur les variables qui ont un impact sur les décisions du modèle. Pour ce faire, nous allons utiliser le package explainerdashboard et shap.

Ce dashboard interactif permet les shap values et donc l'importance des différentes variables sur le modèle.

Dans ce cas, on voit que les factures et l'ancienneté sont les variables les plus importantes pour décider si un client est résilier ou non. Il faut quand même noter que ces variables sont très corrélées, et donc interchangeables, car la facture totale est presque égale au produit de la facture mensuelle moyenne et de l'ancienneté.

Sinon, on voit que la durée du contrat est également très importante.

D'un autre côté, les variables genre, senior et couple ont un impact très faibles sur le modèle, ce qui peut être étonnant, surtout pour la situation conjuguale.

Ce dashboard permet également de voir les shap values pour les clients individuellement et ainsi de comprendre comment l'algorithme prend la décision. On peut aussi, changer la valeur d'un paramètre d'un client pour voir si la décision change.

Le graphique Precision Plot dans l'onglet Classification Stats montre que les décisions sont très souvent bien claires et que la majorité des clients qui résilient sont bien classés. Pour encore améliorer le score, on aurait pu baisser le threshold en dessous de 0.5.

On y voit aussi les courbes de trade-off entre la précision et le recall. Et on voit très clairement que le recall est privilégié par rapport à la précision. Ce que se traduit par une assez mauvaise classification des clients ne souhaitant pas résilier. Cependant, ce n'était pas mon objectif.

Shap Beeswarm

Pour comprendre l'impact positif ou négatif des variables, le package shap fournit le graphique Beeswarm qui illustre bien cela.

Ce graphique montre qu'une facture totale élévée a un impact négatif sur la décision du modèle, à savoir que le client va résilier. Ceci s'explique par le fait que les clients avec une facture totale élevée sont anciens et ont moins de chance de résilier.

Au contraire, une facture mensuelle faible se traduit par plus de résiliation, il y a probablement plusieurs facteurs pouvant expliquer cela, on peut supposer que le fait que les clients avec un "petit" forfait ont plus tendance à en changer.

Le type de contrat à aussi un impact, par exemple, les longs contrats (de 2 ans) sont préférables à ceux de moins de 1 mois.

Conclusion

Ainsi, on a construit un modèle de classification dont l'objectif principal est de déterminer si un client va résilier, dans l'optique d'essayer de le conserver en lui proposant des offres ou en faisant un démarchage plus ciblé. Pour cette tâche l'algorithme utilisé est performant, bien qu'il soit très simple. Il est donc facile d'expliquer les décisions avec les shap values notamment.

Points à améliorer ou à approfondir

Je vais finir par proposer des pistes d'améliorations ou de développements plus complexes.

Repo GitHub du projet: https://github.com/gwatkinson/test-technique-bouygues